#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import re
import struct
from dataclasses import dataclass, field
from typing import List, Dict, Optional, Tuple

from PyQt5 import QtWidgets, QtCore


# ====================== 结构体模型 ======================

@dataclass
class FieldInfo:
    name: str
    c_type: str
    bit_width: Optional[int] = None   # 位段宽度（当前不做精确位域打包）
    array_len: Optional[int] = None   # 数组长度，如 rscv[13]
    kind: str = "scalar"              # scalar / struct / array


@dataclass
class StructInfo:
    name: str
    fields: List[FieldInfo] = field(default_factory=list)


# ====================== 解析 C struct ======================

STRUCT_RE = re.compile(r"struct\s+(\w+)\s*\{(.*?)\};", re.S)
# 支持：类型 名字; / 类型 名字[N]; / 类型 名字:N;
FIELD_RE = re.compile(
    r"^\s*([a-zA-Z_][\w\s\*]*)\s+(\w+)\s*(\[\s*(\d+)\s*\])?\s*(?::\s*(\d+))?\s*;",
    re.M
)


def strip_line_comments(text: str) -> str:
    """去掉 // 注释，方便解析。"""
    return re.sub(r"//.*", "", text)


def parse_structs(text: str) -> Dict[str, StructInfo]:
    clean = strip_line_comments(text)
    structs: Dict[str, StructInfo] = {}

    for name, body in STRUCT_RE.findall(clean):
        s = StructInfo(name=name.strip())
        for c_type, fname, arr_full, arr_len, bits in FIELD_RE.findall(body):
            c_type_norm = " ".join(c_type.split())
            bit_width = int(bits) if bits else None
            array_len = int(arr_len) if arr_len else None
            s.fields.append(FieldInfo(
                name=fname,
                c_type=c_type_norm,
                bit_width=bit_width,
                array_len=array_len
            ))
        structs[s.name] = s

    # 标记 kind
    for s in structs.values():
        for f in s.fields:
            if f.array_len is not None:
                f.kind = "array"
            elif f.c_type in structs:
                f.kind = "struct"
            else:
                f.kind = "scalar"

    return structs


# ====================== 类型映射 & 默认值/范围 ======================

def get_type_range(c_type: str,
                   bit_width: Optional[int]) -> Tuple[Optional[int],
                                                      Optional[int],
                                                      str]:
    """
    返回 (min, max, kind)：
        kind: 'uint' / 'int' / 'float'
    """
    # 位段：简单按无符号处理
    if bit_width is not None:
        return 0, (1 << bit_width) - 1, "uint"

    t = " ".join(c_type.split())

    # 无符号
    if t in ("u8", "unsigned char", "uint8_t"):
        return 0, 0xFF, "uint"
    if t in ("u16", "unsigned short", "unsigned short int", "uint16_t"):
        return 0, 0xFFFF, "uint"
    if t in ("u32", "unsigned int", "unsigned long", "unsigned long int", "uint32_t"):
        return 0, 0xFFFFFFFF, "uint"

    # 有符号
    if t in ("char", "int8_t", "signed char"):
        return -0x80, 0x7F, "int"
    if t in ("short", "short int", "int16_t"):
        return -0x8000, 0x7FFF, "int"
    if t in ("int", "long", "long int", "int32_t"):
        return -0x80000000, 0x7FFFFFFF, "int"

    # 浮点 / double
    if t in ("float", "double"):
        return None, None, "float"

    # 默认 32 位有符号
    return -0x80000000, 0x7FFFFFFF, "int"


def generate_default_value(seq_idx: int, c_type: str, bit_width: Optional[int]):
    """
    给每个字段一个区别开的默认值（用于初始显示）。
    """
    lo, hi, kind = get_type_range(c_type, bit_width)

    if kind == "float":
        # 简单：0.1 * (idx+1)
        return round(0.1 * (seq_idx + 1), 3)

    if kind in ("uint", "int"):
        if lo is None or hi is None:
            return seq_idx + 1
        span = hi - lo + 1
        step_limit = min(span, 1000)
        val = lo + ((seq_idx + 1) % step_limit)
        return val

    return 0


def get_struct_format_char(c_type: str) -> str:
    """
    C 类型 -> struct.pack 格式字符
    """
    t = " ".join(c_type.split())

    # 无符号
    if t in ("u8", "unsigned char", "uint8_t"):
        return "B"
    if t in ("u16", "unsigned short", "unsigned short int", "uint16_t"):
        return "H"
    if t in ("u32", "unsigned int", "unsigned long", "unsigned long int", "uint32_t"):
        return "I"

    # 有符号
    if t in ("char", "int8_t", "signed char"):
        return "b"
    if t in ("short", "short int", "int16_t"):
        return "h"
    if t in ("int", "long", "long int", "int32_t"):
        return "i"

    # 浮点
    if t == "float":
        return "f"
    if t == "double":
        return "d"

    # 默认 32 位有符号
    return "i"


def get_type_size(c_type: str, bit_width: Optional[int]) -> int:
    """
    一个字段（非数组、非 struct）的字节大小。
    当前忽略 bit_width，将位段当作其基本类型大小。
    """
    fmt_char = get_struct_format_char(c_type)
    # 使用标准大小（无对齐）
    return struct.calcsize("<" + fmt_char)


def compute_min_mid_max(c_type: str, bit_width: Optional[int], mode: str):
    """
    mode: 'min' / 'mid' / 'max'
    """
    lo, hi, kind = get_type_range(c_type, bit_width)

    if kind == "float":
        # 浮点给一个简单边界
        if mode == "min":
            return -1.0
        elif mode == "mid":
            return 0.0
        else:
            return 1.0

    if kind in ("uint", "int"):
        if lo is None or hi is None:
            lo, hi = 0, 100
        if mode == "min":
            return lo
        elif mode == "mid":
            return lo + (hi - lo) // 2
        else:
            return hi

    # 默认
    return 0


# ====================== 校验算法 ======================

def xor_check(data: bytes) -> int:
    c = 0
    for b in data:
        c ^= b
    return c & 0xFF


def sum_check(data: bytes) -> int:
    return sum(data) & 0xFF


def crc16_modbus(data: bytes) -> int:
    crc = 0xFFFF
    for b in data:
        crc ^= b
        for _ in range(8):
            if crc & 0x0001:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc & 0xFFFF


# ====================== GUI 主窗口 ======================

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("报文结构体测试数据生成工具 (PyQt, Hex, 大小端 + 校验 + 策略填充)")
        self.resize(1100, 800)

        self.structs: Dict[str, StructInfo] = {}
        self.flat_fields: List[Tuple[str, FieldInfo]] = []   # (path, FieldInfo)
        self.field_edits: Dict[str, QtWidgets.QLineEdit] = {}
        self.current_struct_size: int = 0  # 按紧凑打包的字节数

        self._setup_ui()

    def _setup_ui(self):
        central = QtWidgets.QWidget()
        self.setCentralWidget(central)
        main_layout = QtWidgets.QVBoxLayout(central)

        # 顶部：文件 & 解析 & struct 选择
        top_layout = QtWidgets.QHBoxLayout()
        btn_open = QtWidgets.QPushButton("打开 .h/.cpp 文件")
        btn_open.clicked.connect(self.on_open_file)
        btn_parse = QtWidgets.QPushButton("解析文本中的 struct")
        btn_parse.clicked.connect(self.on_parse_text)
        top_layout.addWidget(btn_open)
        top_layout.addWidget(btn_parse)

        top_layout.addWidget(QtWidgets.QLabel("选择 struct："))
        self.struct_combo = QtWidgets.QComboBox()
        self.struct_combo.currentIndexChanged.connect(self.on_struct_changed)
        top_layout.addWidget(self.struct_combo, stretch=1)

        main_layout.addLayout(top_layout)

        # 源码输入
        main_layout.addWidget(QtWidgets.QLabel("struct 源码（可粘贴）："))
        self.text_source = QtWidgets.QPlainTextEdit()
        self.text_source.setPlaceholderText("在这里粘贴 / 查看 struct 定义...")
        main_layout.addWidget(self.text_source, stretch=2)

        # 结构体大小显示
        self.struct_size_label = QtWidgets.QLabel("结构体理论大小：未知")
        main_layout.addWidget(self.struct_size_label)

        # 字段列表（滚动）
        main_layout.addWidget(QtWidgets.QLabel("字段列表（数组已展开，每项可编辑测试值）："))
        self.scroll_area = QtWidgets.QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        main_layout.addWidget(self.scroll_area, stretch=3)

        self.fields_container = QtWidgets.QWidget()
        self.fields_layout = QtWidgets.QFormLayout(self.fields_container)
        self.fields_layout.setLabelAlignment(QtCore.Qt.AlignLeft)
        self.fields_layout.setFormAlignment(QtCore.Qt.AlignTop)
        self.scroll_area.setWidget(self.fields_container)

        # 控制区域：字节序、校验、策略、生成
        ctrl_layout = QtWidgets.QHBoxLayout()

        # 字节序
        ctrl_layout.addWidget(QtWidgets.QLabel("字节序："))
        self.endian_combo = QtWidgets.QComboBox()
        self.endian_combo.addItem("小端 (LSB first)", "little")
        self.endian_combo.addItem("大端 (MSB first)", "big")
        ctrl_layout.addWidget(self.endian_combo)

        ctrl_layout.addSpacing(20)

        # 校验方式
        ctrl_layout.addWidget(QtWidgets.QLabel("校验方式："))
        self.check_combo = QtWidgets.QComboBox()
        self.check_combo.addItem("无校验", "none")
        self.check_combo.addItem("异或校验 (XOR, 1字节)", "xor")
        self.check_combo.addItem("和校验 (SUM, 1字节)", "sum")
        self.check_combo.addItem("CRC16 (Modbus, 2字节)", "crc16")
        ctrl_layout.addWidget(self.check_combo)

        ctrl_layout.addSpacing(20)

        # 策略选择
        ctrl_layout.addWidget(QtWidgets.QLabel("填充值策略："))
        self.strategy_combo = QtWidgets.QComboBox()
        self.strategy_combo.addItem("不自动填充（保留当前值）", "none")
        self.strategy_combo.addItem("默认递增（1,2,3...）", "default")
        self.strategy_combo.addItem("全部最小值", "min")
        self.strategy_combo.addItem("全部中间值", "mid")
        self.strategy_combo.addItem("全部最大值", "max")
        self.strategy_combo.addItem("边界交替（偶数最小，奇数最大）", "edge_alt")
        self.strategy_combo.addItem("全部 0", "zero")
        ctrl_layout.addWidget(self.strategy_combo)

        btn_apply_strategy = QtWidgets.QPushButton("按策略填充值")
        btn_apply_strategy.clicked.connect(self.on_apply_strategy)
        ctrl_layout.addWidget(btn_apply_strategy)

        ctrl_layout.addStretch(1)

        # 生成 / 复制
        btn_gen = QtWidgets.QPushButton("生成 16 进制报文")
        btn_gen.clicked.connect(self.on_generate_hex)
        ctrl_layout.addWidget(btn_gen)

        btn_copy = QtWidgets.QPushButton("复制 16 进制")
        btn_copy.clicked.connect(self.on_copy_hex)
        ctrl_layout.addWidget(btn_copy)

        main_layout.addLayout(ctrl_layout)

        # 输出
        main_layout.addWidget(QtWidgets.QLabel("生成的 16 进制报文："))
        self.text_output = QtWidgets.QPlainTextEdit()
        self.text_output.setPlaceholderText("点击“生成 16 进制报文”后，结果显示在这里...")
        main_layout.addWidget(self.text_output, stretch=2)

        self.statusBar().showMessage("就绪")

    # ---------- 工具函数：根据当前默认进制格式化数值 ----------

    def format_value_for_display(self, value, c_type: str, bit_width: Optional[int]) -> str:
        """
        根据当前“默认显示进制”的设置，把数值转成字符串：
        - 浮点：始终用 10 进制表示
        - 整数：可 10 进制 或 0xXX 形式
        """
        # 默认显示 10 进制
        _, _, kind = get_type_range(c_type, bit_width)

        if kind == "float" or kind == "double":
            return str(value)  # 显示浮点数十进制

        # 对于整数类型，显示 10 进制值，并在右侧显示 16 进制
        return f"{value} (0x{int(value):X})"
    # ---------- 文件 & 解析 ----------

    def on_open_file(self):
        path, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            "选择头文件或源码文件",
            "",
            "C/C++ Files (*.h *.hpp *.cpp *.c);;All Files (*)"
        )
        if not path:
            return

        try:
            with open(path, "r", encoding="utf-8") as f:
                content = f.read()
        except UnicodeDecodeError:
            with open(path, "r", encoding="gbk", errors="ignore") as f:
                content = f.read()

        self.text_source.setPlainText(content)
        self.parse_structs_from_text(content)

    def on_parse_text(self):
        content = self.text_source.toPlainText()
        if not content.strip():
            QtWidgets.QMessageBox.warning(self, "提示", "请先粘贴或输入 struct 源码。")
            return
        self.parse_structs_from_text(content)

    def parse_structs_from_text(self, text: str):
        self.structs = parse_structs(text)
        self.struct_combo.blockSignals(True)
        self.struct_combo.clear()

        if not self.structs:
            QtWidgets.QMessageBox.warning(self, "解析失败", "未解析到任何 struct 定义，请检查文本。")
            self.clear_fields()
            self.struct_size_label.setText("结构体理论大小：未知")
        else:
            for name in self.structs.keys():
                self.struct_combo.addItem(name)
            self.statusBar().showMessage(f"解析到 {len(self.structs)} 个 struct")

        self.struct_combo.blockSignals(False)

        if self.struct_combo.count() > 0:
            self.on_struct_changed(0)

    # ---------- 字段 UI & 结构体大小 ----------

    def clear_fields(self):
        while self.fields_layout.count():
            item = self.fields_layout.takeAt(0)
            w = item.widget()
            if w:
                w.deleteLater()
        self.flat_fields.clear()
        self.field_edits.clear()

    def on_struct_changed(self, index: int):
        if index < 0:
            return
        name = self.struct_combo.currentText()
        if not name or name not in self.structs:
            self.clear_fields()
            self.struct_size_label.setText("结构体理论大小：未知")
            return

        self.clear_fields()
        self.build_fields_for_struct(name, prefix="")
        self.current_struct_size = self.compute_struct_size(name)
        self.struct_size_label.setText(
            f"结构体理论大小（按紧凑打包，不含校验）：{self.current_struct_size} 字节"
        )

    def build_fields_for_struct(self, struct_name: str, prefix: str):
        s = self.structs.get(struct_name)
        if not s:
            return

        base_idx = len(self.flat_fields)

        for i, f in enumerate(s.fields):
            seq_idx = base_idx + i

            if f.kind == "struct":
                pfx = f"{prefix}{f.name}" if not prefix else f"{prefix}.{f.name}"
                self.build_fields_for_struct(f.c_type, prefix=pfx)

            elif f.kind == "array":
                # 数组：展开
                for k in range(f.array_len or 0):
                    path = (
                        f"{prefix}{f.name}[{k}]"
                        if not prefix
                        else f"{prefix}.{f.name}[{k}]"
                    )
                    label_text = f"{path}  ({f.c_type}[{f.array_len}])"
                    label = QtWidgets.QLabel(label_text)
                    edit = QtWidgets.QLineEdit()
                    default_val = generate_default_value(
                        seq_idx * (f.array_len or 1) + k,
                        f.c_type,
                        f.bit_width
                    )
                    edit.setText(self.format_value_for_display(default_val, f.c_type, f.bit_width))

                    self.fields_layout.addRow(label, edit)
                    self.flat_fields.append((path, f))
                    self.field_edits[path] = edit

            else:
                # 标量
                path = f"{prefix}{f.name}" if not prefix else f"{prefix}.{f.name}"
                label_text = f"{path}  ({f.c_type}"
                if f.bit_width is not None:
                    label_text += f" : {f.bit_width}"
                label_text += ")"

                label = QtWidgets.QLabel(label_text)
                edit = QtWidgets.QLineEdit()
                default_val = generate_default_value(seq_idx, f.c_type, f.bit_width)
                edit.setText(self.format_value_for_display(default_val, f.c_type, f.bit_width))

                self.fields_layout.addRow(label, edit)
                self.flat_fields.append((path, f))
                self.field_edits[path] = edit

    def compute_struct_size(self, struct_name: str) -> int:
        """
        递归计算结构体总字节数（按紧凑排列，不考虑对齐）。
        """
        s = self.structs.get(struct_name)
        if not s:
            return 0

        total = 0
        for f in s.fields:
            if f.kind == "struct":
                total += self.compute_struct_size(f.c_type)
            elif f.kind == "array":
                elem_size = get_type_size(f.c_type, f.bit_width)
                total += elem_size * (f.array_len or 0)
            else:
                total += get_type_size(f.c_type, f.bit_width)
        return total

    # ---------- 策略填充值 ----------
    def on_apply_strategy(self):
        if not self.flat_fields:
            QtWidgets.QMessageBox.warning(self, "提示", "请先解析并选择一个 struct。")
            return

        strategy = self.strategy_combo.currentData()
        if strategy == "none":
            self.statusBar().showMessage("未应用策略（保持原值）")
            return

        for idx, (path, f) in enumerate(self.flat_fields):
            _, _, kind = get_type_range(f.c_type, f.bit_width)

            if strategy == "default":
                value = generate_default_value(idx, f.c_type, f.bit_width)
            elif strategy in ("min", "mid", "max"):
                value = compute_min_mid_max(f.c_type, f.bit_width, strategy)
            elif strategy == "edge_alt":
                edge_mode = "min" if (idx % 2 == 0) else "max"
                value = compute_min_mid_max(f.c_type, f.bit_width, edge_mode)
            elif strategy == "zero":
                value = 0.0 if kind == "float" else 0
            else:
                value = generate_default_value(idx, f.c_type, f.bit_width)

            edit = self.field_edits.get(path)
            if edit is not None:
                edit.setText(self.format_value_for_display(value, f.c_type, f.bit_width))

        self.statusBar().showMessage(f"已应用策略：{self.strategy_combo.currentText()}")
    # ---------- 一键填充最小/中间/最大 ----------

    def fill_by_mode(self, mode: str):
        """
        mode: 'min' / 'mid' / 'max'
        """
        if not self.flat_fields:
            QtWidgets.QMessageBox.warning(self, "提示", "请先解析并选择一个 struct。")
            return

        for path, f in self.flat_fields:
            value = compute_min_mid_max(f.c_type, f.bit_width, mode)
            edit = self.field_edits.get(path)
            if edit is not None:
                edit.setText(self.format_value_for_display(value, f.c_type, f.bit_width))

        self.statusBar().showMessage(f"已填充 {mode} 值")

    # ---------- 报文生成 ----------

    # ---------- 报文生成 ----------

    def on_generate_hex(self):
        if not self.flat_fields:
            QtWidgets.QMessageBox.warning(self, "提示", "请先解析 struct 并选择一个结构体。")
            return

        endian = self.endian_combo.currentData()  # 'little' 或 'big'
        endian_char = "<" if endian == "little" else ">"

        data_bytes = bytearray()

        for idx, (path, f) in enumerate(self.flat_fields):
            edit = self.field_edits.get(path)
            if edit is None:
                continue
            text = edit.text().strip()

            lo, hi, kind = get_type_range(f.c_type, f.bit_width)

            # 解析输入
            if text == "":
                # 空：使用默认值
                value = generate_default_value(0, f.c_type, f.bit_width)
                fmt_char = get_struct_format_char(f.c_type)
                try:
                    packed = struct.pack(endian_char + fmt_char, value)
                except struct.error as e:
                    QtWidgets.QMessageBox.warning(
                        self,
                        "打包错误",
                        f"字段 {path} 打包失败：{e}",
                    )
                    return

            else:
                if kind == "float" or kind == "double":
                    # 支持 float / double 16 进制原始位模式
                    if text.lower().startswith("0x"):
                        try:
                            bit_val = int(text, 16)  # 解析 16 进制
                        except ValueError:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的浮点值 '{text}' 无法解析为 16 进制整数。"
                            )
                            return
                        size = get_type_size(f.c_type, f.bit_width)  # 4 for float, 8 for double
                        try:
                            packed = bit_val.to_bytes(size, byteorder=endian, signed=False)
                        except OverflowError as e:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的位模式超出 {size} 字节范围：{e}"
                            )
                            return
                    else:
                        try:
                            value = float(text)  # 十进制浮点
                        except ValueError:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的浮点值 '{text}' 无法转换为 float/double。"
                            )
                            return
                        fmt_char = get_struct_format_char(f.c_type)
                        try:
                            packed = struct.pack(endian_char + fmt_char, value)
                        except struct.error as e:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "打包错误",
                                f"字段 {path} 打包失败：{e}",
                            )
                            return

                elif kind == "int" or kind == "uint":
                    # 处理负整数和 16 进制整数
                    if text.lower().startswith("0x"):
                        # 负的 16 进制值：如 -0x1F
                        try:
                            value = int(text, 16)
                        except ValueError:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的值 '{text}' 无法转换为整数类型。"
                            )
                            return
                    elif text.startswith("-0x"):
                        # 负的 16 进制数
                        try:
                            value = int(text, 16)
                            value = value & 0xFFFFFFFF  # 补码处理，32 位范围
                        except ValueError:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的负值 '{text}' 无法转换为 16 进制整数。"
                            )
                            return
                    else:
                        try:
                            value = int(text)  # 处理负数：例如 -10
                        except ValueError:
                            QtWidgets.QMessageBox.warning(
                                self,
                                "输入错误",
                                f"字段 {path} 的值 '{text}' 无法转换为整数类型。"
                            )
                            return

                    fmt_char = get_struct_format_char(f.c_type)
                    try:
                        packed = struct.pack(endian_char + fmt_char, value)
                    except struct.error as e:
                        QtWidgets.QMessageBox.warning(
                            self,
                            "打包错误",
                            f"字段 {path} 打包失败：{e}",
                        )
                        return

            data_bytes.extend(packed)

        # 校验
        check_mode = self.check_combo.currentData()
        check_info = "无校验"
        check_len = 0

        if check_mode == "xor":
            c = xor_check(data_bytes)
            data_bytes.append(c)
            check_len = 1
            check_info = f"XOR 校验: 0x{c:02X}"
        elif check_mode == "sum":
            c = sum_check(data_bytes)
            data_bytes.append(c)
            check_len = 1
            check_info = f"SUM 校验: 0x{c:02X}"
        elif check_mode == "crc16":
            c = crc16_modbus(data_bytes)
            data_bytes.append(c & 0xFF)
            data_bytes.append((c >> 8) & 0xFF)
            check_len = 2
            check_info = f"CRC16 (Modbus): 0x{c:04X}"

        total_len = len(data_bytes)
        payload_len = total_len - check_len

        struct_size = self.current_struct_size
        length_match = (payload_len == struct_size)

        hex_str = " ".join(f"{b:02X}" for b in data_bytes)

        out_lines = []
        out_lines.append(f"结构体理论大小（不含校验）：{struct_size} 字节")
        out_lines.append(f"生成数据长度（不含校验）：{payload_len} 字节")
        out_lines.append(f"校验方式：{check_info}")
        out_lines.append(f"生成数据总长度（含校验）：{total_len} 字节")
        if not length_match:
            out_lines.append("⚠ 长度不一致：请检查字段类型/数组长度/对齐方式是否与协议一致")
        else:
            out_lines.append("✅ 数据长度与结构体理论大小一致")

        out_lines.append("")
        out_lines.append(hex_str)

        self.text_output.setPlainText("\n".join(out_lines))
        self.statusBar().showMessage("16 进制报文生成完成")

    def on_copy_hex(self):
        text = self.text_output.toPlainText().strip()
        if not text:
            QtWidgets.QMessageBox.information(self, "提示", "当前没有可复制的报文，请先生成。")
            return
        lines = text.splitlines()
        if lines:
            hex_line = lines[-1]
        else:
            hex_line = text
        QtWidgets.QApplication.clipboard().setText(hex_line)
        self.statusBar().showMessage("16 进制报文已复制到剪贴板")


# ====================== 程序入口 ======================

def main():
    app = QtWidgets.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
